package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"db/models"
	"db/user"
	"encoding/json"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	// Database backends
	_ "db/models/mysql"
)

//Env HTTP服务的整环境变量
type Env struct {
	db     models.DataAccessObject
	cookie sessions.Store
	//tmp map[string]*template.Template
}

func main() {
	log.SetFlags(log.LstdFlags | log.Llongfile)
	//cookie := sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
	//cookie := sessions.NewCookieStore([]byte("SESSION_KEY"))

	//初始化
	cookie := sessions.NewFilesystemStore("./session/", []byte("SESSION_KEY"))
	cookie.MaxAge(60 * 5)
	db:=models.GetDBAdapter()
	if nil==db {
		log.Panic("数据可初始化失败");
	}
	//db, err := models.NewDB("mysql", "user:user@tcp(127.0.0.1:3306)/test?charset=utf8")
	//if err != nil {
	//	log.Panic(err)
	//}
	env := &Env{
		cookie: cookie,
		db:db,
	}
	r := mux.NewRouter()

	r.Use(env.LoggingMiddleware)
	r.HandleFunc("/", env.Index).Methods("GET")
	//r.HandleFunc("/login", env.LoginGet).Methods("GET")
	r.HandleFunc("/check", env.LoginCheck)
	r.HandleFunc("/login", env.LoginPost) //5.Queries("name", "{name}", "password", "{password}")
	r.HandleFunc("/logout", env.Logout).Methods("GET", "POST")

	//ur := r.PathPrefix("/user").Subrouter()
	//ur.HandleFunc("/{id:[0-9]+}/info", env.UserInfo).Methods("GET")
	//ur.HandleFunc("/{id:[0-9]+}", env.User).Methods("GET")
	//ur.HandleFunc("/{id:[0-9]+}", env.UpdateUser).Methods("POST")
	//ur.HandleFunc("/{id:[0-9]+}", env.DeleteUser).Methods("DELETE")
	//ur.HandleFunc("", env.PutUser).Methods("PUT")
	//ur.Path("/all").Methods("GET").HandlerFunc(env.UserAll)//.Queries("limit","{[0-9]*?}","start","{[0-9]*?}")

	r.PathPrefix("/user").Handler(user.MakeMuxer("/user"));
	//r.Handle("/sales",user.MakeMuxer("/sales"))


	r.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("web/"))))
	//打印Handle列表
	_ = r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
		if pathTemplate, err := route.GetPathTemplate(); err == nil {
			log.Println("ROUTE:", pathTemplate)
		}
		if pathRegexp, err := route.GetPathRegexp(); err == nil {
			log.Println("Path regexp:", pathRegexp)
		}
		if queriesTemplates, err := route.GetQueriesTemplates(); err == nil {
			log.Println("Queries templates:", strings.Join(queriesTemplates, ","))
		}
		if queriesRegexps, err := route.GetQueriesRegexp(); err == nil {
			log.Println("Queries regexps:", strings.Join(queriesRegexps, ","))
		}
		if methods, err := route.GetMethods(); err == nil {
			log.Println("Methods:", strings.Join(methods, ","))
		}
		log.Println()
		return nil
	})


	srvHttp := &http.Server{
		Addr:    ":8080",
		Handler: r,
	}
	go func() {
		srvHttp.RegisterOnShutdown(env.OnShutdown)
		if err := srvHttp.ListenAndServe(); err != nil {
			log.Println("HTTP:", err)
		}
	}()

	pool := x509.NewCertPool()
	caCrtBuf, err := ioutil.ReadFile("ca/ca.crt")
	if err != nil {
		log.Panicln("ReadFile err:", err)
	}

	//124420746959206637288558432667825213124
	//caCrt,err:=x509.ParseCertificate(caCrtBuf);
	//x509.ParseCRL
	//pool.AddCert(caCrt)
	if ok := pool.AppendCertsFromPEM(caCrtBuf); !ok {
		log.Panicln("failed to parse user certificate")
	}
	srvHttps := &http.Server{
		Addr:    ":8081",
		Handler: r,
		TLSConfig: &tls.Config{
			ClientCAs:          pool,
			ClientAuth:         tls.RequireAndVerifyClientCert,
			InsecureSkipVerify: false,
		},
	}
	//srvHttps.ErrorLog
	go func() {
		srvHttps.RegisterOnShutdown(env.OnShutdown)
		if err := srvHttps.ListenAndServeTLS("ca/server.crt", "ca/server.key"); err != nil {
			log.Println("HTTPS", err)
		}
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
	<-c
	log.Println("五秒后关闭系统")
	ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
	if err := srvHttps.Shutdown(ctx); err != nil {
		log.Println("HTTPS Shutdown", err)
	}
	if err := srvHttp.Shutdown(ctx); err != nil {
		log.Println("HTTP Shutdown", err)
	}
	<-ctx.Done()
}

func (env *Env) OnShutdown() {
	log.Println("系统关闭通知")
	log.Println("关闭数据库连接。。。")
}

//LoggingMiddleware 中间件
// 1.检测Session是否登录
// 2.如果为HTTPS则获取客户端证书的信息。
func (env *Env) LoggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if origin := r.Header.Get("Origin"); origin != "" {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Credentials", "true")
			w.Header().Set("Access-Control-Allow-Headers","Token, Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
			w.Header().Set("Access-Control-Expose-Headers","Token, Authorization");
		}
		if r.Method == "OPTIONS" {
			//w.WriteHeader(200)
			return
		}

		if r.TLS != nil && r.TLS.HandshakeComplete {
			for _, cert := range r.TLS.PeerCertificates {
				if cert.IsCA == false {
					// Do something with the cert, for example:
					//cert.EmailAddresses
					//cert.Extensions[0].
					//serialNumber:=cert.SerialNumber
					//signature := cert.Signature
					//issuer    := cert.Issuer
					log.Println(cert.Issuer)
					log.Println(cert.EmailAddresses)
					log.Println(cert.SerialNumber)
					log.Println(cert.Subject)
				}
			}
			//&& len(r.TLS.PeerCertificates) > 0 && len(r.TLS.PeerCertificates[0].Subject.Organization) > 0
			//&r.TLS.PeerCertificates[0].Subject.ToRDNSequence()
			//log.Println(r.TLS.PeerCertificates[0].Subject)
			session, _ := env.cookie.Get(r, "session-name")
			session.Values["isLogin"] = false
			session.Values["name"] = r.TLS.PeerCertificates[0].Subject.Organization[0]
			_ = session.Save(r, w)
			next.ServeHTTP(w, r)
		} else if strings.EqualFold(r.URL.Path, "/login") {
			next.ServeHTTP(w, r)
		} else {
			session, _ := env.cookie.Get(r, "session-name")
			w.Header().Set("Token",session.ID);
			if _, ok := session.Values["isLogin"]; ok {
				//每次请求都进行session延期
				w.Header().Set("Authorization",session.Values["name"].(string));
				_ = session.Save(r, w)
				next.ServeHTTP(w, r)
			} else {
				//next.ServeHTTP(w, r)
				http.Error(w,`{"ERROR":401}`, 401)
				w.WriteHeader(401);
				//_ = json.NewEncoder(w).Encode( map[string]interface{}{"url": r.URL.Path});
			}
		}

	})
}


func (env *Env) LoginCheck(w http.ResponseWriter, r *http.Request) {
	session, _ := env.cookie.Get(r, "session-name")
	if _, ok := session.Values["isLogin"]; ok {
		_ = json.NewEncoder(w).Encode(map[string]interface{}{"name": session.Values["name"]})
		return
	}
	w.WriteHeader(401)
	//http.Error(w, "登陆失败", 401)
}
func (env *Env) Index(w http.ResponseWriter, r *http.Request) {
	_ = json.NewEncoder(w).Encode(map[string]interface{}{"url": r.URL.Path, "Hello": "World"})
	return
	//session, _ := env.cookie.Get(r, "session-name")
	//t, _ := template.ParseFiles("template/index.html")
	//_ = t.Execute(w, map[string]interface{}{"Name": session.Values["name"]}) //执行模板的merger操作
}

//LoginPost  处理登录事件
func (env *Env) Logout(w http.ResponseWriter, r *http.Request) {
	session, _ := env.cookie.Get(r, "session-name")
	session.Values["isLogin"] = false
	_ = session.Save(r, w)
}

//LoginPost  处理登录事件
func (env *Env) LoginPost(w http.ResponseWriter, r *http.Request) {

	var user models.User
	if r.Body == nil {
		http.Error(w, "Please send a request body", 400)
		return
	}
	json.NewDecoder(r.Body).Decode(&user)

	//user := &models.User{Name: r.FormValue("username"), Passwd: r.FormValue("password")}
	//json.NewDecoder(r.Body).Decode(user)
	if isLogin, err := env.db.CheckPasswd(user); isLogin {
		session, _ := env.cookie.Get(r, "session-name")
		session.Values["isLogin"] = true
		session.Values["name"] = user.Name
		session.Values[42] = 43
		_ = session.Save(r, w)
		_ = json.NewEncoder(w).Encode( map[string]interface{}{"Message": "登录成功", "IsLogin": true});
		//t, _ := template.ParseFiles("template/login.html")
		//_ = t.Execute(w, map[string]interface{}{"Message": "登录成功", "IsLogin": true}) //执行模板的merger操作
	} else {
		if err == nil {
			http.Error(w, "登陆失败", 401)
		} else {
			http.Error(w, err.Error(), 500)
		}
		//if err == nil {
		//	t, _ := template.ParseFiles("template/login.html")
		//	_ = t.Execute(w, map[string]interface{}{"Message": "登录失败，请重新登录"}) //执行模板的merger操作
		//} else {
		//	http.Error(w, err.Error(), 500)
		//}
	}

}

//LoginGet 返回登录页面
func (env *Env) LoginGet(w http.ResponseWriter, r *http.Request) {
	session, _ := env.cookie.Get(r, "session-name")
	if _, ok := session.Values["isLogin"]; ok {
		_, _ = w.Write([]byte("已登录"))
		return
	}
	t, _ := template.ParseFiles("template/login.html")
	_ = t.Execute(w, map[string]interface{}{"Message": "请登录"}) //执行模板的merger操作
	return
}